#include "ThreadPool.h"

ThreadPool* threadPoolCreate(int min,int max,int queueSize)
{
    ThreadPool* pool=(ThreadPool*)calloc(1,sizeof(ThreadPool));
    do{
        if(NULL==pool)
        {
            perror("calloc:");
            break;
        }
        pool->threadIDs=(pthread_t*)calloc(max,sizeof(pthread_t));
        if(NULL==pool->threadIDs)
        {
            perror("calloc:");
            break;
        }
        pool->minNum=min;
        pool->maxNum=max;
        pool->liveNum=min;
        if(pthread_mutex_init(&pool->mutexPool,NULL)!=0
           ||pthread_mutex_init(&pool->mutexBusy,NULL)!=0
           ||pthread_cond_init(&pool->notEmpty,NULL)!=0
           ||pthread_cond_init(&pool->notFull,NULL)!=0)
        {
            perror("pthread_mutex_init||pthread_cond_init");
            break;
        }
        pool->taskQ=(Task*)calloc(queueSize,sizeof(Task));
        pool->queueCapacity=queueSize;

        // 创建线程
        pthread_create(&pool->managerID,NULL,manager,pool);
        for(int i=0;i<min;++i)
        {
            pthread_create(&pool->threadIDs[i],NULL,worker,pool);
        }
        return pool;
    }while(0);

    if(pool&&pool->threadIDs)free(pool->threadIDs);
    if(pool&&pool->taskQ)free(pool->taskQ);
    if(pool)free(pool);

    return NULL;
}

int threadPoolDestroy(ThreadPool* pool){
    if(NULL==pool)
    {
        return -1;
    }
    pool->shutdown=1;
    pthread_join(pool->managerID,NULL);
    for(int i=0;i<pool->liveNum;++i)
    {
        pthread_cond_signal(&pool->notEmpty);
    }
    if(pool->taskQ)
    {
        free(pool->taskQ);
    } 
    if(pool->threadIDs)
    {
        free(pool->threadIDs);
    }
    pthread_mutex_destroy(&pool->mutexPool);
    pthread_mutex_destroy(&pool->mutexBusy);
    pthread_cond_destroy(&pool->notFull);
    pthread_cond_destroy(&pool->notEmpty);

    free(pool);
    pool=NULL;
    return 0;
}


void threadPoolAdd(ThreadPool* pool,void(*func)(void*),void* arg){
    pthread_mutex_lock(&pool->mutexPool);
    while(pool->queueSize==pool->queueCapacity && !pool->shutdown)
    {
        pthread_cond_wait(&pool->notFull,&pool->mutexPool);  
    }
    if(pool->shutdown)
    {
        pthread_mutex_unlock(&pool->mutexPool);
        return ;
    }
    pool->taskQ[pool->queueRear].function=func;
    pool->taskQ[pool->queueRear].arg=arg;
    pool->queueRear=(pool->queueRear+1)%pool->queueCapacity;
    pool->queueSize++;

    pthread_cond_signal(&pool->notEmpty);
    pthread_mutex_unlock(&pool->mutexPool);
}

int threadPoolBusyNum(ThreadPool* pool)
{
    pthread_mutex_lock(&pool->mutexBusy);
    int busyNum=pool->busyNum;
    pthread_mutex_unlock(&pool->mutexBusy);
    return busyNum;
}
int threadPoolAliveNum(ThreadPool *pool)
{
    pthread_mutex_lock(&pool->mutexPool);
    int liveNum=pool->liveNum;
    pthread_mutex_unlock(&pool->mutexPool);
    return liveNum;
}

void* worker(void *arg)
{
    ThreadPool* pool=(ThreadPool*)arg;

    while(1)
    {
        pthread_mutex_lock(&pool->mutexPool);

        while(pool->queueSize==0 && !pool->shutdown)
        {
            pthread_cond_wait(&pool->notEmpty,&pool->mutexPool);

            if(pool->exitNum>0)
            {
                pool->exitNum--;
                if(pool->liveNum>pool->minNum)
                {
                    pool->liveNum--;
                    pthread_mutex_unlock(&pool->mutexPool);
                    threadExit(pool);
                }
            }
        }
        if(pool->shutdown)
        {
            pthread_mutex_unlock(&pool->mutexPool);
            threadExit(pool);
        }
        Task task;
        task.function=pool->taskQ[pool->queueFront].function;
        task.arg=pool->taskQ[pool->queueFront].arg;

        pool->queueFront=(pool->queueFront+1)%pool->queueCapacity;
        pool->queueSize--;

        pthread_cond_signal(&pool->notFull);
        pthread_mutex_unlock(&pool->mutexPool);
        
        printf("thread %ld start working...\n",pthread_self());
        pthread_mutex_lock(&pool->mutexBusy);
        pool->busyNum++;
        pthread_mutex_unlock(&pool->mutexBusy);
        task.function(task.arg);
        free(task.arg);
        task.arg=NULL;
        
        
        printf("thread %ld end working..\n",pthread_self());
        pthread_mutex_lock(&pool->mutexBusy);
        pool->busyNum--;
        pthread_mutex_unlock(&pool->mutexBusy);
    }
    return NULL;
}

void* manager(void *arg)
{
    ThreadPool* pool=(ThreadPool*)arg;
    while(!pool->shutdown)
    {

        sleep(3);

        pthread_mutex_lock(&pool->mutexPool);
        int queueSize=pool->queueSize;
        int liveNum=pool->liveNum;
        pthread_mutex_unlock(&pool->mutexPool);

        pthread_mutex_lock(&pool->mutexBusy);
        int busyNum=pool->busyNum;
        pthread_mutex_unlock(&pool->mutexBusy);

        // 添加线程
        // 任务的个数>存活的线程个数 && 存活的线程数<最大线程数
        if(queueSize>liveNum && liveNum<pool->maxNum)
        {
            int counter=0;
            pthread_mutex_lock(&pool->mutexPool);
            for(int i=0 ; i<pool->maxNum && pool->liveNum<pool->maxNum ; i++)
            {
                if(pool->threadIDs[i]==0)
                {
                    counter++;
                    pthread_create(&pool->threadIDs[i],NULL,worker,pool);
                    pool->liveNum++;
                }
                if(counter==2)
                {
                    break;
                }
            }
            pthread_mutex_unlock(&pool->mutexPool);
        }
        // 销毁线程
        // 忙的线程*2 < 存活的线程数 && 存活的线程>最小线程数
        if(busyNum*2<liveNum&&liveNum>pool->minNum)
        {
            pthread_mutex_lock(&pool->mutexPool);
            pool->exitNum=1;
            for(int i=0;i<1;i++)
            {
                pthread_cond_signal(&pool->notEmpty);
            }
        }
    }
    return NULL;
}

void threadExit(ThreadPool *pool){
    pthread_t tid=pthread_self();
    for(int i=0;i<pool->maxNum;i++)
    {
        if(pool->threadIDs[i]==tid)
        {
            pool->threadIDs[i]=0;
            printf("threadExit() called, %ld exiting...\n", tid);
            break;
        }
    }
    pthread_exit(NULL);
}

